home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
music
/
midiplay.lzh
/
MP_INTP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-30
|
16KB
|
613 lines
/*
* File: mp_intp.c
* SGoldthorpe 20-Jul-91
*/
/*
* mp_intp - the midi file interpreter for midiplay
* This software is (C) 1991 Stephen Goldthorpe but it's FREE! Usual
* disclaimers and notices about this software not being sold for profit.
* But you may take all you want from the code though! If you have any
* suggestions/bug fixes please get in contact with me. I don't want to
* maintain code i've never even seen before (life's hard enough without all
* of that)!
* -Steve Goldthorpe
* Phone (DAYTIME UK): +44 707 382350
* Internet E-Mail: SGoldthorpe.wgc-e@rx.xerox.com
* goldthor@arisia.xerox.com
*
* Version 0.5 by Piet van Oostrum <piet@cs.ruu.nl>
* November 1991.
* I made the following changes:
* 1. Files > 32767 wouldn't play. I have changed a couple of ints to
* LONG. Also malloc'ed the buffer with a variable rather than a fixed size.
* 2. replaced array referenced with a[i] rather than *(a+i). I find this
* more readable but it has the same meaning.
* 3. Midifiles of more than about 3 minutes didn't play right. I think
* this is a bug in ltod (long to double) in the floating point lib. I
* changed the timing from using floating point arithmetic to LONG
* arithmetic.
* Anyway the timing was not robust in the presence of tempo changes
* because the new time per beat would be applied to the time from the
* beginning of the piece rather than from the time of the tempo change.
* 4. I introduced the -T option to send timing commands. This can be
* used to trigger a drum computer or an arranger.
*
* NOTE: I distribute this version with the consent of Steve
* Goldthorpe. I think he should not be bothered with bugs in my version!
*/
#include <stdio.h>
#include <types.h>
#include <time.h>
#include <string.h>
/* and for the atari OS stuff */
#include <osbind.h>
/* #include "midiplay.h" included by mp_gbls.h */
#include "mp_gbls.h"
/* GLOBAL VARIABLES */
/* The track_delta's are in units of 1/division clockticks */
/* time_per_beat is the length of a quarter note in clock ticks */
static long track_delta[MAX_TRACKS], division, time_per_beat;
static BYTE *track_pos[MAX_TRACKS];
static long track_left[MAX_TRACKS];
static int track_finished[MAX_TRACKS];
static clock_t clock_orig;
static char *gFile;
static WORD format, tracks;
static int finished_tracks;
static int trnr;
static long clock_delta, clock_time;
/* FUNCTION DECLS */
BOOL interp();
static void truncated(), all_notes_off(), parse_error();
/* MACRO FUNCTIONS */
/* the error checking may be a bit OTT but I'm gonna do it anyway (helps
catch those naughty bugs - and bad files) */
#define GET32BITS(dw,p,l) dw=(((LONG)(*p)<<24)+ \
((LONG)(*(p+1))<<16)+ \
(((LONG)*(p+2))<<8)+ \
(LONG)(*(p+3))); \
if(l<4) \
{ truncated(); \
return(FALSE); \
}; \
p += 4; l -= 4
#define GET16BITS(w,p,l) w=(((WORD)(*p)<<8)+(WORD)*(p+1)); \
if(l<2) \
{ truncated(); \
return(FALSE); \
} \
p += 2; l -= 2
#define GET8BITS(b,p,l) b = *(p)++; \
if(--l<0) \
{ truncated(); \
return(FALSE); \
}
#define GETVARLEN(dw,p,l) for(dw=(LONG)(*p)&0x7f;(*(p)++)&0x80;) \
{ if(--l<0) \
{ truncated(); \
return(FALSE); \
}; \
dw <<=7; \
dw |= (LONG)(*p)&0x7f; \
}; \
if(--l<0) \
{ truncated(); \
return(FALSE); \
}
#define CHECKLEFT(l,v) if(l<v) \
{ truncated(); \
return(FALSE); \
}
#define SEND(b) Bconout(3,b)
/* FUNCTION DEFS */
BOOL interp(buffer, file, len)
char *file;
BYTE *buffer;
LONG len;
{ BYTE *pos=buffer,*next;
BYTE running_status[MAX_TRACKS],last_running_status;
WORD w;
LONG dw,delta;
LONG left=len;
gFile=file;
/* check header */
if((left<4)||(strncmp("MThd",(char*)pos,4)!=0))
{ (void)fprintf(stderr,"%s: %s is not a midi file\n",app_name,file);
return(FALSE);
};
pos += 4; left -= 4;
/* find address of next chunk */
GET32BITS(dw,pos,left);
next=pos+dw;
#ifdef DEBUG
(void)printf("pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
next-buffer);
#endif
/* get file format */
GET16BITS(format,pos,left);
switch (format)
{ /* OK we accept formats 0 and 1 */
case 0:
case 1:
break;
/* but we don't do any others */
default:
(void)fprintf(stderr,"%s: can't play %s, midi file type %d\n",app_name,
file,format);
return(FALSE);
};
/* get number of tracks */
GET16BITS(tracks,pos,left);
/* check tracks in range */
if(tracks > MAX_TRACKS)
{ (void)fprintf(stderr,"%s: %s has too many tracks (%d allowed).\n",
app_name,file,tracks,MAX_TRACKS);
return(FALSE);
};
/* get division - this is the division of a quarter note or if negative is
frame based. */
GET16BITS(w,pos,left);
division=(LONG)w;
/* I don't suport the frame stuff yet! */
if(division < 0)
{ (void)fprintf(stderr,
"%s: file %s - don't support framed based files yet!", app_name,
file);
return(FALSE);
};
#ifdef DEBUG
(void)printf("division = %f\n",division);
#endif
time_per_beat = (LONG)(CLK_TCK/2); /* default 120bpm = 2bps */
#ifdef DEBUG
(void)printf("time per beat (1/%d sec) %ld\n", CLK_TCK, time_per_beat);
#endif
/* do some initialisation, track finding etc */
finished_tracks = 0;
for(trnr=0;trnr<tracks;trnr++)
{ CHECKLEFT(left,(next-pos));
left -= next-pos; pos = next;
/* track start */
if((left<4)||(strncmp("MTrk",(char*)pos,4)!=0))
{ (void)fprintf(stderr,"%s: %s parse error, track expected\n",
app_name,file);
#ifdef DEBUG
(void)printf("posn %lx, bytes around (-3..3) %02x %02x %02x %02x \
%02x %02x %02x\n",pos-buffer,*(pos-3),*(pos-2),*(pos-1),*pos,*(pos+1),*(pos+2),
*(pos+3));
#endif
return(FALSE);
};
pos += 4; left -= 4;
/* find address of next chunk */
GET32BITS(dw,pos,left);
track_pos[trnr] = pos;
next = pos+dw;
track_left[trnr] = dw;
#ifdef DEBUG
(void)printf("pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
next-buffer);
#endif
/* get initial delta time */
GETVARLEN(dw,track_pos[trnr],track_left[trnr]);
track_delta[trnr]=dw * time_per_beat;
track_finished[trnr]=FALSE;
running_status[trnr] = 0xfe;
last_running_status = 0xfe;
};
/* let the user know what's happening */
(void)printf("playing '%s' with %d %s\n",file,tracks,
tracks==1?"track":"tracks");
/* get start time */
clock_orig=clock();
clock_time=0;
if (f_Timing) SEND (0xFA);
/* dispatcher */
while (finished_tracks != tracks)
{ BYTE event;
clock_delta = (clock()-clock_orig)*division;
if (f_Timing && clock_delta >= clock_time) {
SEND(0xF8);
clock_time += (time_per_beat * division / 24);
}
for(trnr=0;trnr<tracks;trnr++)
{ if (!track_finished[trnr] & (clock_delta >= track_delta[trnr]))
{ GET8BITS(event,track_pos[trnr],track_left[trnr]);
/* parse event */
switch (event)
{ /* meta-events */
case 0xff:
{ if(!meta_event())
return(FALSE);
break;
};
/* sysex events */
case 0xf0:
case 0xf7:
{ if(!sysex_event(event))
return(FALSE);
break;
};
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
case 0xf5:
case 0xf6:
{ if(!system_common(event))
return(FALSE);
break;
};
case 0xf8:
case 0xf9:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfd:
case 0xfe:
{ if(!system_real_time())
return(FALSE);
break;
};
/* midi events */
default:
{ switch(event & 0xf0)
{ /* 3 byte events */
case 0x90:
case 0x80:
case 0xa0:
case 0xb0:
case 0xe0:
{ BYTE c;
SEND(event);